package com.botarena.server.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;

import com.botarena.server.dao.BotDAO;
import com.botarena.server.dao.ContestDAO;
import com.botarena.server.dao.filter.Filter;
import com.botarena.server.dao.helper.NonTransactional;
import com.botarena.server.dao.helper.NonTransactionalReturn;
import com.botarena.server.model.Account;
import com.botarena.server.model.Bot;
import com.botarena.server.model.Contest;
import com.botarena.server.services.validation.ContestValidator;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;

/**
 * Static ContestService.
 */
public class ContestService extends
		GenericService<Contest, ContestDAO, ContestValidator> {

	private static final DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();

	/**
	 * Returns list of active (i.e. started and not ended) contests filtered
	 * from DAO.
	 */
	public static List<Contest> getActiveContests() {
		return DAOService.execute(new NonTransactionalReturn<List<Contest>>() {

			@Override
			public List<Contest> doOutsideTransaction(PersistenceManager pm) {
				List<Contest> contests = new ContestDAO(pm)
						.getAll(new Filter<Contest>() {

							@Override
							public boolean accept(Contest e) {
								Date now = new Date();
								return e.isActive() && e.getFrom().before(now)
										&& e.getTo().after(now);
							}
						});

				return new ArrayList<Contest>(pm.detachCopyAll(contests));
			}
		});
	}
	
	/**
	 * Gets contestant number
	 * @return
	 */
	public int userNum() {//TODO: per each contest
		int userNum = DAOService.execute(new NonTransactionalReturn<Long>() {

			@Override
			public Long doOutsideTransaction(PersistenceManager pm) {
				Query query = new Query("__Stat_Kind__");
				query.addFilter("kind_name", Query.FilterOperator.EQUAL,
						Account.class.getSimpleName());
				Entity globalStat = datastore.prepare(query).asSingleEntity();
				// FIXME: strasznie czasochlonna operacja! ~jakrawcz
				if (globalStat == null
						|| globalStat.getProperty("count") == null)
					return -1L;

				return (Long) globalStat.getProperty("count");
			}

		}).intValue();
		
		return userNum;
	}
	
	/**
	 * Gets bot number
	 * @return
	 */
	public int botNum() {//TODO: per each contest
		int botNum = DAOService.execute(new NonTransactionalReturn<Long>() {

			@Override
			public Long doOutsideTransaction(PersistenceManager pm) {
				Query query = new Query("__Stat_Kind__");
				query.addFilter("kind_name", Query.FilterOperator.EQUAL,
						Bot.class.getSimpleName());
				Entity globalStat = datastore.prepare(query).asSingleEntity();
				// FIXME: strasznie czasochlonna operacja! ~jakrawcz
				if (globalStat == null
						|| globalStat.getProperty("count") == null)
					return -1L;

				return (Long) globalStat.getProperty("count");
			}

		}).intValue();
		
		return botNum;
	}
	
	/**
	 * Get info about all contests
	 * @return
	 */
	public List<Contest> getAllContests() {

		List<Contest> results = DAOService
				.execute(new NonTransactionalReturn<List<Contest>>() {

					@Override
					public List<Contest> doOutsideTransaction(
							PersistenceManager pm) {
						ContestDAO dao = new ContestDAO(pm);

						List<Contest> contests = dao.getAll();

						return new ArrayList<Contest>(pm.detachCopyAll(contests));
					}

				});

		return results;
	}

	public void createNewContest(final String name, final Date fromDate,
			final Date toDate) {
		DAOService.execute(new NonTransactional() {

			@Override
			public void doOutsideTransaction(PersistenceManager pm) {
				ContestDAO contestDAO = new ContestDAO(pm);

				Contest contest = new Contest(name);
				contest.setFrom(fromDate);
				contest.setTo(toDate);
				contest.setActive(true);
				contestDAO.create(contest);

			}
		});
	}

	/**
	 * Get first ranking page and all bot keys
	 * 
	 * @param key
	 * @param size
	 * @return
	 */
	public List<Bot> getRanking(final String key, final int size) {

		List<Bot> result = DAOService
				.execute(new NonTransactionalReturn<List<Bot>>() {

					@Override
					public List<Bot> doOutsideTransaction(
							PersistenceManager pm) {

						Key contestKey = KeyFactory.stringToKey(key);
						BotDAO botDAO = new BotDAO(pm);

						Collection<Bot> bots = botDAO.getRanking(contestKey);

						return new ArrayList<Bot>(pm.detachCopyAll(bots));
					}

				});

		return result;
	}
	
	/**
	 * Get top N bots (for the first page)
	 * 
	 * @param key
	 * @param size
	 * @return
	 */
	public List<Bot> getRankingPart(final String key, final int beg, final int size) {
		List<Bot> result = DAOService
				.execute(new NonTransactionalReturn<List<Bot>>() {

					@Override
					public List<Bot> doOutsideTransaction(
							PersistenceManager pm) {
					
						Key contestKey = KeyFactory.stringToKey(key);
						BotDAO botDAO = new BotDAO(pm);

						Collection<Bot> bots = botDAO.getRankingPart(
								contestKey, beg, size);
						return new ArrayList<Bot>(pm.detachCopyAll(bots));
					}
				});

		return result;
	}

	/**
	 * Get part of the ranking by keys
	 * 
	 * @param botKeys
	 * @return
	 */
	public List<Bot> getRankingPart(final List<String> botKeys) {

		List<Bot> result = DAOService
				.execute(new NonTransactionalReturn<List<Bot>>() {

					@Override
					public List<Bot> doOutsideTransaction(
							PersistenceManager pm) {

						BotDAO botDAO = new BotDAO(pm);

						List<Bot> bots = new ArrayList<Bot>(botKeys
								.size());

						for (String key : botKeys) {
							bots.add(botDAO.getByKey(key));
						}

						return new ArrayList<Bot>(pm.detachCopyAll(bots));
					}
				});

		return result;

	}
}
